package com.woniuxy.portal.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import cn.hutool.jwt.signers.JWTSigner;
import cn.hutool.jwt.signers.JWTSignerUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.code.kaptcha.Constants;
import com.woniuxy.portal.common.Result;
import com.woniuxy.portal.common.constant.RedisConstant;
import com.woniuxy.portal.common.enums.impl.BusinessCode;
import com.woniuxy.portal.common.exception.Asserts;
import com.woniuxy.portal.common.exception.BusinessException;
import com.woniuxy.portal.entity.User;
import com.woniuxy.portal.model.dto.RegisterDTO;
import com.woniuxy.portal.model.dto.UserDTO;
import com.woniuxy.portal.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author 作者
 * @since 2023-09-15
 */
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {

    @Autowired
    private IUserService userService;
    @Value("${jwt.secret}")
    private String secret;
    @Value("${jwt.timeout}")
    private int timeout;
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 登录
     * 返回状态码：
     *   21000  验证码为空
     *   21001  验证码错误
     *   21002  用户名或密码错误，登录失败
     *
     *  自定义断言：
     *  1、Spring提供的断言接口：org.springframework.util.Assert.hasText();
     *  2、减少程序的if判断，避免臃肿的代码
     */
    @PostMapping("/login")
    public Result login(@RequestBody UserDTO userDTO, HttpSession session){
        // 判断验证码是否为空
        Asserts.error(!StringUtils.hasText(userDTO.getValidateCode()),BusinessCode.VERRIFY_CODE_IS_NULL);

        // 校验验证码是否正确
        Asserts.error(!userDTO.getValidateCode().equals(session.getAttribute(Constants.KAPTCHA_SESSION_KEY)),
                BusinessCode.VERRIFY_CODE_ERROR);

        // 校验账号、密码
        User user = userService.login(userDTO.getAccount(),userDTO.getPassword());
        Asserts.error(Objects.isNull(user), BusinessCode.LOGIN_FAILED);
        // 登录成功，用户先存储到session中
        // session.setAttribute("user",user);

        // 生成token
        String token = createToken(user);
        // 把token存储到redis中
        redisTemplate.opsForValue().set(
                RedisConstant.USER_TOKEN_PREFIX+user.getId(),token,timeout, TimeUnit.MINUTES);
        // 返回token
        return Result.ok(token);
    }

    /**
     * 生成token的方法
     */
    private String createToken(User user) {
        // 设置签名
        JWTSigner jwtSigner = JWTSignerUtil.hs256(secret.getBytes());
        // token中存放的数据
        Map<String,Object> payload = new HashMap<>();
        payload.put("userId",user.getId());
        payload.put("userName",user.getNickname());
        String token = JWTUtil.createToken(payload, jwtSigner);
        return token;
    }

    /**
     * 登录后，查询realName名称
     */
    @GetMapping("/getCurrentUser")
    public Result getCurrentUser(@RequestHeader("Authorization") String authorization){
        // 截取token
        String token = authorization.replace("Bearer ","");
        // 解析token
        JWT jwt = JWTUtil.parseToken(token);
        // 获取token中的userId，判断token是否存在
        Object userId = jwt.getPayload("userId");
        Object userName = jwt.getPayload("userName");
        if (!redisTemplate.hasKey(RedisConstant.USER_TOKEN_PREFIX + userId)) {
            return Result.ok();
        }
        return Result.ok(userName);
    }

    /**
     * 注册
     */
    @PostMapping("/register")
    public Result register(@RequestBody RegisterDTO registerDTO){

        //1. Redis中验证码校验
        String redisCode = (String) redisTemplate.opsForValue().get(registerDTO.getEmail());
        Asserts.error(!StringUtils.hasText(redisCode),BusinessCode.VERRIFY_CODE_IS_NULL);
        Asserts.error(!redisCode.equals(registerDTO.getValicode()),BusinessCode.VERRIFY_CODE_ERROR);

        //2. 账户唯一性校验  select count(1) from user where account='xx'
        long accountNum = userService.count(Wrappers.lambdaQuery(User.class).eq(User::getAccount,registerDTO.getAccount()));
        Asserts.error(accountNum>=1,BusinessCode.USER_EXISTS);

        //3. 邮箱唯一性校验
        long emailNum = userService.count(Wrappers.lambdaQuery(User.class).eq(User::getEmail,registerDTO.getEmail()));
        Asserts.error(emailNum>=1,BusinessCode.EMAIL_EXISTS);

        //4. 保存数据库
        User user = BeanUtil.copyProperties(registerDTO,User.class);
        user.setCreatetime(new Date());
        user.setUpdatetime(new Date());
        user.setState(1);
        userService.save(user);

        //5. 删除Redis中验证码
        redisTemplate.delete(registerDTO.getEmail());
        return Result.ok();
    }
}
